package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constant.RedisConstant;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.PointsStatisticsVO;
import com.tianji.learning.enums.PointsRecordType;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.service.IPointsRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author hrz
 * @since 2025-08-31
 */
@SuppressWarnings("all")
@Service
@RequiredArgsConstructor
public class PointsRecordServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements IPointsRecordService {

    private final RedisTemplate redisTemplate;

    @Override
    public void addRecord(Long userId, Integer points, PointsRecordType type) {
        //将对应事务发来的积分给存储起来
        PointsRecord p = new PointsRecord();
        //1.先判断该类型积分是否有上限
        if (type.getMaxPoints() > 0) {
            //2.有上限就先获取日期
            LocalDateTime startTime = DateUtils.getDayStartTime(LocalDateTime.now());
            LocalDateTime endTime = DateUtils.getDayEndTime(LocalDateTime.now());
            //3.去数据库查询今日已获取的总分
            QueryWrapper<PointsRecord> queryWrapper = new QueryWrapper<>();
            queryWrapper.select("sum(points) as points")
                    .eq("user_id", userId)
                    .eq("type", type.getValue())
                    .between("create_time", startTime, endTime);
            PointsRecord one = this.getOne(queryWrapper);//今日的总分被包裹在points字段中
            if (one.getPoints() >= type.getMaxPoints()) {
                //之前的积分就超过了上限,直接return结束
                return;
            } else {
                if (one.getPoints() + points > type.getMaxPoints()) {
                    //超过了上限,就添加到刚满
                    points = type.getMaxPoints() - one.getPoints();
                }
            }
        }
        //4.如果没有上限就将所有的积分都添加到数据库
        p.setUserId(userId);
        p.setType(type.getValue());
        p.setPoints(points);
        p.setCreateTime(LocalDateTime.now());
        this.save(p);
        //5.将个人积分增加到redis的zset中 - key是固定前缀加上年份和日期
        String key = RedisConstant.POINTS_BOARD_KEY_PREFIX +
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        //使用redis中的zincreby命令 - 增加分数 - 每次触发积分的增加时都同步更新redis
        Double v = redisTemplate.opsForZSet().
                incrementScore(key, userId.toString(), points);
    }

    @Override
    public List<PointsStatisticsVO> queryTodayAllType() {
        //去数据库查询所有本用户今天的所有类型的积分记录
        List<PointsStatisticsVO> pointsStatisticsVOList = new ArrayList<>();
        //1.获取当前用户id
        Long user = UserContext.getUser();
        if (user == null) {
            throw new BizIllegalException("登录信息过期,请重新登录!");
        }
        LocalDateTime startTime = DateUtils.getDayStartTime(LocalDateTime.now());
        LocalDateTime endTime = DateUtils.getDayEndTime(LocalDateTime.now());
        //2.去数据库查询所有该用户所有类型的总积分
        QueryWrapper<PointsRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("type,sum(points) as points")
                .eq("user_id", user)
                .between("create_time", startTime, endTime)
                .groupBy("type"); //根据type分组
        List<PointsRecord> pointsRecords = this.list(queryWrapper);
        //3.封装
        for (PointsRecord pointsRecord : pointsRecords) {
            Integer type = pointsRecord.getType();
            PointsRecordType pointsRecordType = PointsRecordType.of(type);
            PointsStatisticsVO pv = new PointsStatisticsVO();
            pv.setType(pointsRecordType.getDesc());
            pv.setPoints(pointsRecord.getPoints());
            pv.setMaxPoints(pointsRecordType.getMaxPoints() > 0 ?
                    pointsRecordType.getMaxPoints() : pointsRecord.getPoints());
            pointsStatisticsVOList.add(pv);
        }
        return pointsStatisticsVOList;
    }
}
